home *** CD-ROM | disk | FTP | other *** search
-
-
- /*
- * drivers/char/atari_MFPser.c: Atari MFP serial ports implementation
- *
- * Copyright 1994 Roman Hodek
- * EMail: rnhodek@cip.informatik.uni-erlangen.de (Internet)
- * or: Roman_Hodek@n.maus.de (MausNet, NO mail > 16 KB!)
- * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- *
- */
-
-
- /* This file implements the MFP serial ports. These come in two
- * flavors: with or without control lines (RTS, CTS, DTR, ...). They
- * are distinguished by having two different types, MFP_CTRL and
- * MFP_BARE, resp. Most of the low-level functions are the same for
- * both, but some differ.
- *
- * Note that some assumptions are made about where to access the
- * control lines. If the port type is MFP_CTRL, the input lines (CTS
- * and DCD) are assumed to be in the MFP GPIP register, bits 1 and 2.
- * The output lines (DTR and RTS) have to be in the Soundchip Port A,
- * bits 3 and 4. This is the standard ST/TT assigment. If Atari will
- * build a serial port in future, that uses other registers, you have
- * to rewrite this code. But if the port type is MFP_BARE, no such
- * assumptions are necessary. All registers needed are fixed by the
- * MFP hardware. The only parameter is the MFP base address. This is
- * used to implement Serial1 for the TT and the (not connected) MFP
- * port of the Falcon.
- *
- */
-
- #include <linux/types.h>
- #include <linux/sched.h>
- #include <linux/interrupt.h>
- #include <linux/errno.h>
- #include <linux/tty.h>
- #include <linux/termios.h>
- #include <linux/atarihw.h>
- #include <linux/atariints.h>
-
- #include "atari_serial.h"
- #include "atari_MFPser.h"
-
-
-
- /***************************** Prototypes *****************************/
-
- static void MFPser_rx_int( struct intframe *fp, struct async_struct *info );
- static void MFPser_rxerr_int( struct intframe *fp, struct async_struct
- *info );
- static void MFPser_tx_int( struct intframe *fp, struct async_struct *info );
- static void MFPctrl_dcd_int( struct intframe *fp, struct async_struct *info
- );
- static void MFPctrl_cts_int( struct intframe *fp, struct async_struct *info
- );
- static void MFPser_init( struct async_struct *info );
- static void MFPser_deinit( struct async_struct *info, int leave_dtr );
- static void MFPser_enab_tx_int( struct async_struct *info, int enab_flag );
- static int MFPser_check_custom_divisor( struct async_struct *info, int
- divisor );
- static void MFPser_change_speed( struct async_struct *info );
- static void MFPser_restart( struct async_struct *info, int ch );
- static void MFPctrl_throttle( struct async_struct *info, int status );
- static void MFPbare_throttle( struct async_struct *info, int status );
- static void MFPser_set_break( struct async_struct *info, int break_flag );
- static void MFPser_get_serial_info( struct async_struct *info, struct
- serial_struct *retinfo );
- static unsigned int MFPctrl_get_modem_info( struct async_struct *info );
- static unsigned int MFPbare_get_modem_info( struct async_struct *info );
- static int MFPctrl_set_modem_info( struct async_struct *info, int new_dtr,
- int new_rts );
- static int MFPbare_set_modem_info( struct async_struct *info, int new_dtr,
- int new_rts );
-
- /************************* End of Prototypes **************************/
-
-
-
- /* SERIALSWITCH structures for MFP ports
- * Most functions are common to MFP ports with or without control lines
- */
-
- static SERIALSWITCH MFPctrl_switch = {
- MFPser_init, MFPser_deinit, MFPser_enab_tx_int,
- MFPser_check_custom_divisor, MFPser_change_speed,
- MFPser_restart, MFPctrl_throttle, MFPser_set_break,
- MFPser_get_serial_info, MFPctrl_get_modem_info,
- MFPctrl_set_modem_info, NULL
- };
-
-
- static SERIALSWITCH MFPbare_switch = {
- MFPser_init, MFPser_deinit, MFPser_enab_tx_int,
- MFPser_check_custom_divisor, MFPser_change_speed,
- MFPser_restart, MFPbare_throttle, MFPser_set_break,
- MFPser_get_serial_info, MFPbare_get_modem_info,
- MFPbare_set_modem_info, NULL
- };
-
- /* MFP Timer Modes divided by 2 (this already done in the BAUD_BASE */
- int MFP_timer_modes[] = { 2, 5, 8, 25, 32, 50, 100 };
-
- /* Divisors for standard speeds */
- int MFP_baud_table[15] = {
- /* B0 */ 0,
- /* B50 */ 768,
- /* B75 */ 512,
- /* B110 */ 350, /* really 109.71 bps */
- /* B134 */ 286, /* really 134.27 bps */
- /* B150 */ 256,
- /* B200 */ 192,
- /* B300 */ 128,
- /* B600 */ 64,
- /* B1200 */ 32,
- /* B1800 */ 21,
- /* B2400 */ 16,
- /* B4800 */ 8,
- /* B9600 */ 4,
- /* B19200 */ 2
- };
-
-
- void atari_init_MFPser( struct async_struct *info, SERTYPE type,
- int tt_flag, void *ri_addr,
- unsigned char ri_bitno, unsigned char ri_active )
-
- {
- /* set ISRs, but don't enable interrupts yet (done in init()) */
- add_isr( tt_flag ? IRQ_TT_MFP_SEREMPT : IRQ_MFP_SEREMPT,
- (isrfunc)MFPser_tx_int, 0, info );
- add_isr( tt_flag ? IRQ_TT_MFP_RECFULL : IRQ_MFP_RECFULL,
- (isrfunc)MFPser_rx_int, 0, info );
- add_isr( tt_flag ? IRQ_TT_MFP_RECERR : IRQ_MFP_RECERR,
- (isrfunc)MFPser_rxerr_int, 0, info );
- /* Tx_err interrupt unused (it signals only that the Tx shift reg
- * is empty)
- */
-
- if (type == MFP_CTRL && !tt_flag) {
- add_isr( IRQ_MFP_DCD, (isrfunc)MFPctrl_dcd_int, 0, info );
- add_isr( IRQ_MFP_CTS, (isrfunc)MFPctrl_cts_int, 0, info );
- /* clear RTS and DTR */
- GIACCESS( GI_RTS | GI_DTR );
- }
-
- info->type = type;
- info->base = tt_flag ? &tt_mfp : &mfp;
- info->RI.addr = (volatile unsigned char *)ri_addr;
- info->RI.bitno = ri_bitno;
- info->RI.active_state = ri_active;
- info->sw = (type == MFP_CTRL) ? &MFPctrl_switch :
- &MFPbare_switch;
-
- currMFP(info)->rcv_stat = 0; /* disable Rx */
- currMFP(info)->trn_stat = 0; /* disable Tx */
- }
-
-
- static void MFPser_rx_int( struct intframe *fp, struct async_struct *info )
-
- { int ch, stat, err;
-
- stat = currMFP(info)->rcv_stat;
- ch = currMFP(info)->usart_dta;
- /* Frame Errors don't cause a RxErr IRQ! */
- err = (stat & RSR_FRAME_ERR) ? TTY_FRAME : 0;
-
- rs_put_char_to_queue( info, ch, err );
- rs_rx_throttle_if_needed( info );
- }
-
-
- static void MFPser_rxerr_int( struct intframe *fp, struct async_struct *info )
-
- { int ch, stat, err;
-
- stat = currMFP(info)->rcv_stat;
- ch = currMFP(info)->usart_dta; /* most probably junk data */
-
- if (stat & RSR_PARITY_ERR)
- err = TTY_PARITY;
- else if (stat & RSR_OVERRUN_ERR)
- err = TTY_OVERRUN;
- else if (stat & RSR_BREAK_DETECT)
- err = TTY_BREAK;
- else if (stat & RSR_FRAME_ERR) /* should not be needed */
- err = TTY_FRAME;
- else
- err = 0;
-
- rs_put_char_to_queue( info, ch, err );
- rs_rx_throttle_if_needed( info );
- }
-
-
- static void MFPser_tx_int( struct intframe *fp, struct async_struct *info )
-
- { int ch;
-
- if (currMFP(info)->trn_stat & TSR_BUF_EMPTY) {
- if ((ch = rs_get_char_from_queue( info )) < 0) return;
- currMFP(info)->usart_dta = ch;
- rs_tx_wakeup_if_needed( info );
- }
- }
-
-
- static void MFPctrl_dcd_int( struct intframe *fp, struct async_struct *info )
-
- {
- /* Toggle active edge to get next change of DCD! */
- currMFP(info)->active_edge ^= GPIP_DCD;
-
- rs_dcd_changed( info, !(currMFP(info)->par_dt_reg & GPIP_DCD) );
- }
-
-
- static void MFPctrl_cts_int( struct intframe *fp, struct async_struct *info )
-
- {
- /* Toggle active edge to get next change of CTS! */
- currMFP(info)->active_edge ^= GPIP_CTS;
-
- if (rs_cts_changed( info, !(currMFP(info)->par_dt_reg & GPIP_CTS) ))
- /* Don't know yet if this is really necessary, or if a Tx
- * IRQ is delivered anyway when turning on again.
- */
- MFPser_tx_int( fp, info );
- }
-
-
- static void MFPser_init( struct async_struct *info )
-
- {
- /* base value for UCR */
- currMFP(info)->usart_ctr = UCR_PARITY_OFF | UCR_ASYNC_1 |
- UCR_CHSIZE_8 | UCR_PREDIV;
-
- /* enable Rx and clear any error conditions */
- currMFP(info)->rcv_stat = RSR_RX_ENAB;
-
- /* enable Tx */
- currMFP(info)->trn_stat = TSR_TX_ENAB;
-
- /* enable Rx, RxErr and Tx interrupts */
- currMFP(info)->int_en_a |= 0x1c;
- currMFP(info)->int_mk_a |= 0x1c;
-
- if (info->type == MFP_CTRL) {
-
- /* set RTS and DTR (low-active!) */
- GIACCESS( ~(GI_RTS | GI_DTR) );
-
- /* Set active edge of CTS and DCD signals depending on their
- * current state.
- * If the line status changes between reading the status and
- * enabling the interrupt, this won't work :-( How could it be
- * done better??
- */
- if (currMFP(info)->par_dt_reg & GPIP_CTS)
- currMFP(info)->active_edge &= ~GPIP_CTS;
- else
- currMFP(info)->active_edge |= GPIP_CTS;
-
- if (currMFP(info)->par_dt_reg & GPIP_DCD)
- currMFP(info)->active_edge &= ~GPIP_DCD;
- else
- currMFP(info)->active_edge |= GPIP_DCD;
-
- /* enable CTS and DCD interrupts */
- currMFP(info)->int_en_b |= 0x06;
- currMFP(info)->int_mk_b |= 0x06;
- }
- }
-
-
- static void MFPser_deinit( struct async_struct *info, int leave_dtr )
-
- {
- /* disable Rx, RxErr and Tx interrupts */
- currMFP(info)->int_en_a &= ~0x1c;
-
- if (info->type == MFP_CTRL) {
- /* disable CTS and DCD interrupts */
- currMFP(info)->int_en_b &= ~0x06;
- }
-
- /* disable Rx and Tx */
- currMFP(info)->rcv_stat = 0;
- currMFP(info)->trn_stat = 0;
-
- /* wait for last byte to be completely shifted out */
- while( !(currMFP(info)->trn_stat & TSR_LAST_BYTE_SENT) )
- ;
-
- if (info->type == MFP_CTRL) {
- /* drop RTS and DTR if required */
- MFPser_RTSoff();
- if (!leave_dtr)
- MFPser_DTRoff();
- }
-
- /* read Rx status and data to clean up */
- (void)currMFP(info)->rcv_stat;
- (void)currMFP(info)->usart_dta;
- }
-
-
- static void MFPser_enab_tx_int( struct async_struct *info, int enab_flag )
-
- {
- if (enab_flag)
- currMFP(info)->int_en_a |= 0x04;
- else
- currMFP(info)->int_en_a &= ~0x04;
- }
-
-
- static int MFPser_check_custom_divisor( struct async_struct *info,
- int divisor )
-
- { int i;
-
- /* divisor must be a multiple of 2 or 5 (because of timer modes) */
- if (divisor == 0 || ((divisor & 1) && (divisor % 5) != 0)) return( -1 );
-
- /* Determine what timer mode would be selected and look if the
- * timer value would be greater than 256
- */
- for( i = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1; i >= 0; --i )
- if (divisor % MFP_timer_modes[i] == 0) break;
- if (i < 0) return( -1 ); /* no suitable timer mode found */
-
- return( divisor / MFP_timer_modes[i] <= 256 );
- }
-
-
- static void MFPser_change_speed( struct async_struct *info )
-
- { unsigned cflag, baud, chsize, stopb, parity, aflags;
- unsigned div, timer_val;
- int timer_mode;
-
- if (!info->tty || !info->tty->termios) return;
-
- cflag = info->tty->termios->c_cflag;
- baud = cflag & CBAUD;
- chsize = cflag & CSIZE;
- stopb = cflag & CSTOPB;
- parity = cflag & (PARENB | PARODD);
- aflags = info->flags & ASYNC_SPD_MASK;
-
- if (baud == 15 && aflags == ASYNC_SPD_CUST)
- div = info->custom_divisor;
- else {
- /* Maximum MFP speed is 19200 :-( */
- if (baud > 14) baud = 14;
- div = MFP_baud_table[baud];
- }
-
- if (!div) {
- /* speed == 0 -> drop DTR */
- MFPser_DTRoff();
- return;
- }
-
- /* compute timer value and timer mode (garuateed to succeed, because
- * the divisor was checked before by check_custom_divisor(), if it
- * is used-supplied)
- */
- for( timer_mode = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1;
- timer_mode >= 0; --timer_mode )
- if (div % MFP_timer_modes[timer_mode] == 0) break;
- timer_val = div / MFP_timer_modes[timer_mode];
-
- cli();
- /* disable Rx and Tx while changing parameters */
- currMFP(info)->rcv_stat = 0;
- currMFP(info)->trn_stat = 0;
-
- /* stop timer D to set new timer value immediatly after re-enabling */
- currMFP(info)->tim_ct_cd &= ~0x07;
- currMFP(info)->tim_dt_d = timer_val;
- currMFP(info)->tim_ct_cd |= (timer_mode+1);
-
- currMFP(info)->usart_ctr =
- ( (parity & PARENB) ?
- ((parity & PARODD) ? UCR_PARITY_ODD : UCR_PARITY_EVEN) :
- UCR_PARITY_OFF ) |
- ( chsize == CS5 ? UCR_CHSIZE_5 :
- chsize == CS6 ? UCR_CHSIZE_6 :
- chsize == CS7 ? UCR_CHSIZE_7 :
- UCR_CHSIZE_8 ) |
- ( stopb ? UCR_ASYNC_2 : UCR_ASYNC_1 ) |
- UCR_PREDIV;
-
- /* re-enable Rx and Tx */
- currMFP(info)->rcv_stat = RSR_RX_ENAB;
- currMFP(info)->trn_stat = TSR_TX_ENAB;
- sti();
- }
-
-
- static void MFPser_restart( struct async_struct *info, int ch )
-
- {
- if (ch >= 0 && currMFP(info)->trn_stat & TSR_BUF_EMPTY)
- currMFP(info)->usart_dta = ch;
- }
-
-
- static void MFPctrl_throttle( struct async_struct *info, int status )
-
- {
- if (status == TTY_THROTTLE_RQ_FULL)
- MFPser_RTSoff();
- else if (status == TTY_THROTTLE_RQ_AVAIL)
- MFPser_RTSon();
- }
-
-
- static void MFPbare_throttle( struct async_struct *info, int status )
-
- {
- /* no-op */
- }
-
-
- static void MFPser_set_break( struct async_struct *info, int break_flag )
-
- {
- if (break_flag)
- currMFP(info)->trn_stat |= TSR_SEND_BREAK;
- else
- currMFP(info)->trn_stat &= ~TSR_SEND_BREAK;
- }
-
-
- static void MFPser_get_serial_info( struct async_struct *info,
- struct serial_struct *retinfo )
-
- {
- retinfo->baud_base = MFP_BAUD_BASE;
- retinfo->custom_divisor = info->custom_divisor;
- }
-
-
- static unsigned int MFPctrl_get_modem_info( struct async_struct *info )
-
- { unsigned gpip, gi;
-
- cli();
- gpip = currMFP(info)->par_dt_reg;
- gi = GIACCESS( 0 );
- sti();
-
- /* DSR is not connected on the Atari, assume it to be set;
- * RI is tested by the RI bitpos field of info, because the RI is
- * signalled at different ports on TT and Falcon
- */
- return( ((gi & GI_RTS ) ? TIOCM_RTS : 0) |
- ((gi & GI_DTR ) ? TIOCM_DTR : 0) |
- ((gpip & GPIP_DCD) ? TIOCM_CAR : 0) |
- ((gpip & GPIP_CTS) ? TIOCM_CTS : 0) |
- TIOCM_DSR |
- (TEST_BITPOS( info->RI ) ? TIOCM_RNG : 0)
- );
- }
-
-
- static unsigned int MFPbare_get_modem_info( struct async_struct *info )
-
- {
- return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR );
- }
-
-
- static int MFPctrl_set_modem_info( struct async_struct *info,
- int new_dtr, int new_rts )
-
- {
- if (new_dtr == 0)
- MFPser_DTRoff();
- else if (new_dtr == 1)
- MFPser_DTRon();
-
- if (new_rts == 0)
- MFPser_RTSoff();
- else if (new_rts == 1)
- MFPser_RTSon();
-
- return( 0 );
- }
-
-
- static int MFPbare_set_modem_info( struct async_struct *info,
- int new_dtr, int new_rts )
-
- {
- /* no-op */
-
- /* Is it right to return an error or should the attempt to change
- * DTR or RTS be silently ignored?
- */
- return( -EINVAL );
- }
-
-
-
-